/*
 *  xmp_facade.cpp
 *  Wilson
 *
 *  Created by Sebastian Orfino on 11/27/11.
 *  Copyright 2011 lbs3. All rights reserved.
 *
 */

#include "VCPlugInHeaders.h"

//sdk includes
#include "ILayoutUIUtils.h"
#include "IDocument.h"
#include "IMetaDataAccess.h"
#include "MetaDataTypes.h"
#include "StreamUtil.h"
#include "FileUtils.h"
#include "IMetaDataIterator.h"

#include "adobe/zuid.hpp"
#include "adobe/unicode.hpp"

//project includes
#include "xmp_facade.h"
#include "WilID.h"

//using namespace adobe::afl;

xmp::wilson_metadata::wilson_metadata(const UIDRef& d) : docRef(d)
{
	InterfacePtr<IDocument> doc(docRef,UseDefaultIID());
	InterfacePtr<IMetaDataAccess> meta(doc, UseDefaultIID());
	ASSERT_FAIL(!meta);

	CmdUtils::AutoUndoSequencePtr seq(::GetDataBase(doc));
	
	PMString prefix(kWilsonXMPPrefix);
	prefix.SetTranslatable(kFalse);
	
	meta->RegisterNamespace(kWilsonXMPNamespace, prefix, prefix);
	
}

void xmp::wilson_metadata::dump() const
{
	InterfacePtr<IMetaDataAccess> meta(docRef, UseDefaultIID());
	ASSERT_FAIL(!meta);
	
	
	/*IPMStream* stream, const bool16& package = kTrue,
	 const bool16& allowInPlaceModification = kTrue,
	 const bool16& expandable = kTrue,ical	 const bool16& packetDisabled = kFalse) const = 0;
	 */
	
	IDFile idfile;
	FileUtils::GetCurrentFolder(&idfile);
	FileUtils::AppendPath(&idfile,"metadata.xmp");
	//(WideString("metadata.xmp"));
	InterfacePtr<IPMStream> stream(StreamUtil::CreateFileStreamWrite(idfile));
	if (stream)
		bool16 err = meta->SaveToStream(stream);
	
}

void xmp::wilson_metadata::agregar_comentario(xmp::info_comentario_t& info)
{
	InterfacePtr<IDocument> doc(docRef,UseDefaultIID());
	
	if (!doc) return;
	
	InterfacePtr<IMetaDataAccess> meta(doc, UseDefaultIID());
	ASSERT_FAIL(!meta);
	
	InterfacePtr<IMetaDataIterator> it(meta->Enumerate(kWilsonXMPNamespace,info_nota_t::bag_tag(),IMetaDataAccess::kIterJustChildren),UseDefaultIID());
	if (!it) return;
	PMString currentNS, currentPropPath, currentVal;
	IMetaDataAccess::IteratorOptions options;

	PMString id(info.id_nota.c_str());
	
	while (it->Next(currentNS,currentPropPath,currentVal,&options) == kTrue) {
	
		PMString valor;
		meta->GetStructField(kWilsonXMPNamespace,currentPropPath,info_nota_t::xmp_ns(),info_nota_t::id_tag(), valor);
		
		if (valor!=id) continue;
		
		CmdUtils::AutoUndoSequencePtr seq(::GetDataBase(doc));
		
		//me fijo si tiene el bag de comentarios y si no lo tiene lo creo.
		PMString pathComentariosBag;
		meta->ComposeStructFieldPath(kWilsonXMPNamespace,currentPropPath,info_nota_t::xmp_ns(),info_nota_t::comentarios_tag(),pathComentariosBag);
		if (meta->PropertyExists(kWilsonXMPNamespace,pathComentariosBag) == kFalse)
			meta->Set(kWilsonXMPNamespace,pathComentariosBag,PMString(),metadata::kPropValueIsArray);
		
		//agrego un item nuevo al bag
		meta->AppendArrayItem(kWilsonXMPNamespace,pathComentariosBag,"",metadata_bag,metadata::kPropValueIsStruct);
		PMString pathUltimoItem;
		meta->ComposeArrayItemPath(kWilsonXMPNamespace, pathComentariosBag, metadata::kArrayLastItem,pathUltimoItem);
		
		//y por ultimo lleno la estructura con la data en "info".
		adobe::zuid_t uid;
		info.id = uid.str(); //un id nuevo, no se si es util en realidad.
		meta->SetStructField(kWilsonXMPNamespace,pathUltimoItem, info_comentario_t::xmp_ns(),info_comentario_t::id_tag(), info.id); 
		meta->SetStructField(kWilsonXMPNamespace,pathUltimoItem, info_comentario_t::xmp_ns(),info_comentario_t::texto_tag(), info.texto);
		meta->SetStructField(kWilsonXMPNamespace,pathUltimoItem, info_comentario_t::xmp_ns(),info_comentario_t::creador_tag(), info.creador);
		meta->SetStructField(kWilsonXMPNamespace,pathUltimoItem, info_comentario_t::xmp_ns(),info_comentario_t::cuando_tag(), info.cuando);
		meta->SetStructField(kWilsonXMPNamespace,pathUltimoItem, info_comentario_t::xmp_ns(),info_comentario_t::id_nota_tag(), info.id_nota);
		
		
		
		break; //ya no continuo, en teoria solo hay una nota con un id determinado.
	}
	
	
	
}

std::vector<xmp::info_comentario_t> xmp::wilson_metadata::listar_comentarios(const xmp::info_nota_t& in_nota) const 
{
	using namespace std;
	using namespace xmp;
	
	vector<info_comentario_t> comentarios_v;
	do {
		if (!in_nota.id.length()) break;
		
		InterfacePtr<IDocument> doc(docRef,UseDefaultIID());
		if (!doc) break;
		
		InterfacePtr<IMetaDataAccess> meta(doc, UseDefaultIID());
		if (!meta) break;
		
		//primero itero entre todas las notas hasta que encuentro la nota que estoy buscando para extraer sus comentarios.
		InterfacePtr<IMetaDataIterator> it(meta->Enumerate(kWilsonXMPNamespace,info_nota_t::bag_tag(),IMetaDataAccess::kIterJustChildren),UseDefaultIID());
		if (!it) break;
		PMString currentNS, currentPropPath, currentVal;
		IMetaDataAccess::IteratorOptions options;
		
		while (it->Next(currentNS,currentPropPath,currentVal,&options) == kTrue) {
					
			PMString valor;
			meta->GetStructField(kWilsonXMPNamespace,currentPropPath,info_nota_t::xmp_ns(),info_nota_t::id_tag(), valor);
			string id_nota = valor.GetPlatformString();
			
			if (id_nota != in_nota.id) continue; //sigo loopeando mientras no lo encuentre.
			
			meta->ComposeStructFieldPath(kWilsonXMPNamespace,currentPropPath,info_comentario_t::xmp_ns(),info_nota_t::comentarios_tag(),valor);
			
			//llegue al nodo nota que buscaba.
			//creo el iterador para recorrer todos sus comentarios.
			InterfacePtr<IMetaDataIterator> coment_it(meta->Enumerate(kWilsonXMPNamespace,valor,IMetaDataAccess::kIterJustChildren),UseDefaultIID());
			if (!coment_it) continue;
			
			PMString currentNS2,currentPropPath2,currentVal2;
			IMetaDataAccess::IteratorOptions options2;
			
			//y recorro todos los comentarios de la nota y voy populando el vector.
			while(coment_it->Next(currentNS2,currentPropPath2,currentVal2,&options2) == kTrue) {

				info_comentario_t comentario; //el que construyo para agregar al vector;
				PMString aux;
				meta->GetStructField(kWilsonXMPNamespace,currentPropPath2,info_comentario_t::xmp_ns(),info_comentario_t::id_nota_tag(),aux);
				comentario.id_nota = aux.GetPlatformString();

				//todo: CUIDADO, EL TEXTO ESTA EN UNICODE EN EL XMP, GETPLATFORMSTRING() NO FUNCA BIEN!!!!
				//GetPlatformString() solo es seguro cuando uso base64 o guids (chars hexa) o ISO-8601 para las fechas.
				meta->GetStructField(kWilsonXMPNamespace,currentPropPath2,info_comentario_t::xmp_ns(), info_comentario_t::texto_tag(),aux);
				adobe::to_utf8(aux.begin(), aux.end(), std::back_inserter(comentario.texto));

				meta->GetStructField(kWilsonXMPNamespace,currentPropPath2,info_comentario_t::xmp_ns(), info_comentario_t::creador_tag(),aux);
				adobe::to_utf8(aux.begin(), aux.end(), std::back_inserter(comentario.creador));
				
				meta->GetStructField(kWilsonXMPNamespace,currentPropPath2,info_comentario_t::xmp_ns(), info_comentario_t::cuando_tag(),aux);
				comentario.cuando = aux.GetPlatformString();
				
				meta->GetStructField(kWilsonXMPNamespace,currentPropPath2,info_comentario_t::xmp_ns(), info_comentario_t::id_tag(),aux);
				comentario.id = aux.GetPlatformString();
				
			
				comentarios_v.push_back(comentario);
			}
			break; //si ya lo encontre no sigo loopeando..
		}
		
		
	} while(false);
	
	return comentarios_v;
}

std::vector<xmp::info_nota_t> xmp::wilson_metadata::listar_notas() const
{
	using namespace std;
	using namespace xmp;
	
	
	vector<info_nota_t> notas_v;
	do {
		InterfacePtr<IDocument> doc(docRef,UseDefaultIID());
		if (!doc) break;
		
		InterfacePtr<IMetaDataAccess> meta(doc, UseDefaultIID());
		if (!meta) break;
		
		InterfacePtr<IMetaDataIterator> it(meta->Enumerate(kWilsonXMPNamespace,info_nota_t::bag_tag(),IMetaDataAccess::kIterJustChildren),UseDefaultIID());
		if (!it) break;
		PMString currentNS, currentPropPath, currentVal;
		IMetaDataAccess::IteratorOptions options;
		
		while (it->Next(currentNS,currentPropPath,currentVal,&options) == kTrue) {
			/*
			 std::string id; ///el id de la nota sale del IWilNoteData
			 std::string mini; ///la mini se hace con el snapshot_helper
			 std::string creador; ///el usuario IC que creo la nota
			 std::string cuando; ///cuando
			 */
			
			info_nota_t nota;
			PMString valor;
			meta->GetStructField(kWilsonXMPNamespace,currentPropPath,info_nota_t::xmp_ns(),info_nota_t::id_tag(), valor);
			nota.id = valor.GetPlatformString();
			
			meta->GetStructField(kWilsonXMPNamespace,currentPropPath,info_nota_t::xmp_ns(),info_nota_t::mini_tag(), valor);
			nota.mini = valor.GetPlatformString();
			
			meta->GetStructField(kWilsonXMPNamespace,currentPropPath,info_nota_t::xmp_ns(),info_nota_t::creador_tag(), valor);
			adobe::to_utf8(valor.begin(), valor.end(), std::back_inserter(nota.creador));
			
			meta->GetStructField(kWilsonXMPNamespace,currentPropPath,info_nota_t::xmp_ns(),info_nota_t::cuando_tag(), valor);
			nota.cuando = valor.GetPlatformString();
			
			meta->GetStructField(kWilsonXMPNamespace,currentPropPath,info_nota_t::xmp_ns(),info_nota_t::color_tag(), valor);
			nota.color_hex = valor.GetPlatformString();
			
			PMString auxPathField;
			meta->ComposeStructFieldPath(kWilsonXMPNamespace,currentPropPath,kWilsonXMPNamespace,info_nota_t::tiene_marcador_tag(),auxPathField);
			meta->GetBoolean(kWilsonXMPNamespace,auxPathField,nota.tiene_marcador);
			
			notas_v.push_back(nota);
		}
	} while(false);
	
	return notas_v;
}

bool16 xmp::wilson_metadata::eliminar_nota(const info_nota_t& in_nota) const
{
	using namespace xmp;
	using namespace std;
	
	do {
		if (!in_nota.id.length()) break;
		
		InterfacePtr<IDocument> doc(docRef,UseDefaultIID());
		if (!doc) break;
		
		InterfacePtr<IMetaDataAccess> meta(doc, UseDefaultIID());
		if (!meta) break;
		
		//primero itero entre todas las notas hasta que encuentro la nota que estoy buscando para extraer sus comentarios.
		InterfacePtr<IMetaDataIterator> it(meta->Enumerate(kWilsonXMPNamespace,info_nota_t::bag_tag(),IMetaDataAccess::kIterJustChildren),UseDefaultIID());
		if (!it) break;
		PMString currentNS, currentPropPath, currentVal;
		IMetaDataAccess::IteratorOptions options;
		int32 index = 0;
		while (it->Next(currentNS,currentPropPath,currentVal,&options) == kTrue) {
			
			index++;
			PMString valor;
			meta->GetStructField(kWilsonXMPNamespace,currentPropPath,info_nota_t::xmp_ns(),info_nota_t::id_tag(), valor);
			string id_nota = valor.GetPlatformString();
			
			if (id_nota != in_nota.id) continue; //sigo loopeando mientras no lo encuentre.
		
			/**
			 Delete an array item.
			 @param	ns			IN	The namespace of the array. Must not be an empty string.
			 @param	arrayName	IN	The name of the array.
			 @param	index		IN	The index of the array item. Arrays are indexed from 1. 
			 Use metadata::kArrayLastItem for the last item in the array.
			 
			virtual void DeleteArrayItem(const PMString& ns, const PMString& arrayName, int32 index) = 0;
			 Delete a field within a structure.
			 @param	ns			IN	The namespace of the structure. Must not be an empty string.
			 @param	structName	IN	The name of the structure.
			 @param	fieldNs		IN	The namespace of the field. Maybe be an empty string.
			 @param	fieldName	IN	The name of the field.
			
			virtual void DeleteStructField(const PMString& ns, const PMString& structName, 
										   const PMString& fieldNs, const PMString& fieldName) = 0;*/
			
			meta->DeleteArrayItem(kWilsonXMPNamespace,info_nota_t::bag_tag(),index);
			return kTrue;
		}
			
		
	} while(false);
		
	return kFalse;
}


void xmp::wilson_metadata::modificar_nota(const info_nota_t& in_nota) const
{
	
	using namespace xmp;
	using namespace std;
	
	do {
		if (!in_nota.id.length()) break;
		
		InterfacePtr<IDocument> doc(docRef,UseDefaultIID());
		if (!doc) break;
		
		InterfacePtr<IMetaDataAccess> meta(doc, UseDefaultIID());
		if (!meta) break;
		
		//primero itero entre todas las notas hasta que encuentro la nota que estoy buscando para extraer sus comentarios.
		InterfacePtr<IMetaDataIterator> it(meta->Enumerate(kWilsonXMPNamespace,info_nota_t::bag_tag(),IMetaDataAccess::kIterJustChildren),UseDefaultIID());
		if (!it) break;
		PMString currentNS, currentPropPath, currentVal;
		IMetaDataAccess::IteratorOptions options;
		
		while (it->Next(currentNS,currentPropPath,currentVal,&options) == kTrue) {
		
			PMString valor;
			meta->GetStructField(kWilsonXMPNamespace,currentPropPath,info_nota_t::xmp_ns(),info_nota_t::id_tag(), valor);
			string id_nota = valor.GetPlatformString();
			
			if (id_nota != in_nota.id) continue; //sigo loopeando mientras no lo encuentre.

			//se puede poner el valor a los campos de dos formas. la primer forma es la que uso al setear el campo "id"
			PMString auxPathField;
			meta->ComposeStructFieldPath(kWilsonXMPNamespace,currentPropPath,kWilsonXMPNamespace,"id",auxPathField);
			meta->Set(kWilsonXMPNamespace,auxPathField,in_nota.id);
			
			//y esta es la otra forma, que es mas conveniente.
			meta->SetStructField(kWilsonXMPNamespace,currentPropPath, info_nota_t::xmp_ns(),info_nota_t::mini_tag(), in_nota.mini);
			meta->SetStructField(kWilsonXMPNamespace,currentPropPath,info_nota_t::xmp_ns(),info_nota_t::cuando_tag(),in_nota.cuando);
			meta->SetStructField(kWilsonXMPNamespace,currentPropPath,info_nota_t::xmp_ns(),info_nota_t::creador_tag(),in_nota.creador);
			meta->SetStructField(kWilsonXMPNamespace,currentPropPath,info_nota_t::xmp_ns(),info_nota_t::color_tag(), in_nota.color_hex);
			
			meta->ComposeStructFieldPath(kWilsonXMPNamespace,currentPropPath,kWilsonXMPNamespace,info_nota_t::tiene_marcador_tag(),auxPathField);
			meta->SetBoolean(kWilsonXMPNamespace,auxPathField,in_nota.tiene_marcador);
			
		}
		
	} while(false);
	
}

void xmp::wilson_metadata::crear_nota(const xmp::info_nota_t& info)
{
	InterfacePtr<IDocument> doc(docRef,UseDefaultIID());
	
	if (!doc) return;
	 
	InterfacePtr<IMetaDataAccess> meta(doc, UseDefaultIID());
	ASSERT_FAIL(!meta);
	
	CmdUtils::AutoUndoSequencePtr seq(::GetDataBase(doc));
	
	//si el doc tadavia no tiene data de notas, genero la primera.
	PMString dummy;
	if (meta->Get(kWilsonXMPNamespace, info_nota_t::bag_tag(),dummy) == kFalse)
		meta->Set(kWilsonXMPNamespace,info_nota_t::bag_tag(),PMString(),metadata::kPropValueIsArray);
	
	meta->AppendArrayItem(kWilsonXMPNamespace, info_nota_t::bag_tag(),"",metadata_bag,metadata::kPropValueIsStruct);
	
	PMString pathUltimoItem;
	meta->ComposeArrayItemPath(kWilsonXMPNamespace, info_nota_t::bag_tag(), metadata::kArrayLastItem,pathUltimoItem);
	
	//se puede poner el valor a los campos de dos formas. la primer forma es la que uso al setear el campo "id"
	PMString auxPathField;
	meta->ComposeStructFieldPath(kWilsonXMPNamespace,pathUltimoItem,kWilsonXMPNamespace,"id",auxPathField);
	meta->Set(kWilsonXMPNamespace,auxPathField,info.id);
	
	//y esta es la otra forma, que es mas conveniente.
	meta->SetStructField(kWilsonXMPNamespace,pathUltimoItem, info_nota_t::xmp_ns(),info_nota_t::mini_tag(), info.mini);
	meta->SetStructField(kWilsonXMPNamespace,pathUltimoItem,info_nota_t::xmp_ns(),info_nota_t::cuando_tag(),info.cuando);
	meta->SetStructField(kWilsonXMPNamespace,pathUltimoItem,info_nota_t::xmp_ns(),info_nota_t::creador_tag(),info.creador);
	meta->SetStructField(kWilsonXMPNamespace,pathUltimoItem,info_nota_t::xmp_ns(),info_nota_t::color_tag(), info.color_hex);
	
	meta->ComposeStructFieldPath(kWilsonXMPNamespace,pathUltimoItem,kWilsonXMPNamespace,info_nota_t::tiene_marcador_tag(),auxPathField);
	meta->SetBoolean(kWilsonXMPNamespace,auxPathField,kTrue);
	
}